package craky.component;

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.concurrent.FutureTask;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JProgressBar;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.border.LineBorder;

import craky.util.WindowMove;

public class JLoadingDialog extends JDialog implements ActionListener
{
    private static final long serialVersionUID = -6555677237581881143L;
    
    /**
     * �������
     */
    private Container contentPane;

    /**
     * ������
     */
    private JProgressBar progressBar;

    /**
     * ��ʾ��ʾ��Ϣ��Label
     */
    private JLabel lbInfo;

    /**
     * ȡ����ť
     */
    private JButton btnCancel;

    /**
     * ������
     */
    private Window parent;
    
    /**
     * ���ʵ�ֲ��ֹ���ļ�����
     */
    private ComponentListener listener;
    
    /**
     * ��Ҫ�����������Ķ���
     */
    private Runnable command;
    
    /**
     * ��������ʾ��Ϣ
     */
    private String statusInfo;

    /**
     * �Ƿ���ʾ��ȡ������ť
     */
    private boolean cancelButtonVisible;
    
    /**
     * �����Ƿ�ȡ��
     */
    private boolean cancelled;
    
    /**
     * �������ʱִ��
     */
    private Action finishedAction;
    
    /**
     * ������ȡ��ʱִ��
     */
    private Action cancelAction;
    
    /**
     * �̹߳������
     */
    private ScheduledThreadPoolExecutor executor;
    
    /**
     * ִ�������Future
     */
    private ScheduledFuture<?> future;
    
    /**
     * ����ж����ڵ�ʵ��
     */
    private WindowMove move;
    
    /**
     * ���췽��
     * @param parent ������
     */
    public JLoadingDialog(Window parent)
    {
        this(parent, null, null);
    }
    
    /**
     * ���췽��
     * @param parent ������
     * @param command ��Ҫ�����������Ķ���
     * @param statusInfo ��ǰ�̶߳�����ʾ��Ϣ
     */
    public JLoadingDialog(Window parent, Runnable command, String statusInfo)
    {
        this(parent, command, statusInfo, true);
    }
    
    /**
     * ���췽��
     * @param parent ������
     * @param command ��Ҫ�����������Ķ���
     * @param statusInfo ��ǰ�̶߳�����ʾ��Ϣ
     * @param cancelButtonVisible �Ƿ���ʾ��ȡ������ť
     */
    public JLoadingDialog(Window parent, Runnable command, String statusInfo, boolean cancelButtonVisible)
    {
        super(parent, ModalityType.DOCUMENT_MODAL);
        this.move = new WindowMove(this);
        this.parent = parent;
        this.command = command;
        this.statusInfo = statusInfo;
        this.cancelButtonVisible = cancelButtonVisible;
        initUI();
    }
    
    private void initUI()
    {
        contentPane = getContentPane();
        progressBar = new JRound3DProgressBar();
        lbInfo = new JLabel("<html>" + statusInfo);
        btnCancel = new JButton(UIManager.getString("OptionPane.cancelButtonText"));
        listener = new ComponentAdapter()
        {
            public void componentResized(ComponentEvent e)
            {
                reLayout();
            }
        };
        
        setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
        progressBar.setPreferredSize(new Dimension(-1, 15));
        progressBar.setIndeterminate(true);
        btnCancel.setPreferredSize(new Dimension(75, 22));
        btnCancel.addActionListener(this);
        btnCancel.setVisible(cancelButtonVisible);
        lbInfo.setVerticalAlignment(JLabel.TOP);
        contentPane.setLayout(null);
        contentPane.add(progressBar);
        contentPane.add(lbInfo);
        contentPane.add(btnCancel);
        contentPane.addComponentListener(listener);
        setUndecorated(true);
        setResizable(false);
        setBorder(new LineBorder(new Color(200, 230, 230), 2, false));
        setSize(390, 100);
        setLocationRelativeTo(parent);
        
        addWindowListener(new WindowAdapter()
        {
            public void windowClosing(WindowEvent e)
            {
                actionPerformed(null);
            }
        });
    }
    
    private void reLayout()
    {
        int width = contentPane.getWidth();
        int height = contentPane.getHeight();
        Dimension buttonSize = btnCancel.getPreferredSize();
        Dimension barSize = progressBar.getPreferredSize();
        int labelY = 30 + barSize.height;
        progressBar.setBounds(15, 20, width - 30, barSize.height);
        lbInfo.setBounds(15, labelY, width - 30, height - 10 - labelY);
        
        if(btnCancel.isVisible())
        {
            btnCancel.setBounds(width - buttonSize.width - 10, height - buttonSize.height - 10, buttonSize.width, buttonSize.height);
        }
    }
    
    public void changeContentPane(Container newContentPane)
    {
        contentPane.removeComponentListener(listener);
        newContentPane.removeAll();
        newContentPane.setLayout(null);
        newContentPane.addComponentListener(listener);
        
        for(Component c: contentPane.getComponents())
        {
            newContentPane.add(c);
        }
        
        setContentPane(newContentPane);
        getRootPane().doLayout();
        this.contentPane = newContentPane;
    }
    
    public void runCommand(Runnable command)
    {
        this.command = command;
        runCommand();
    }
    
    public void runCommand()
    {
        cancelled = false;
        Task<?> task = new Task<Object>(command, null);
        executor = new ScheduledThreadPoolExecutor(1);
        future = executor.schedule(task, 0, TimeUnit.MILLISECONDS);
        setVisible(true);
    }
    
    public void stopCommand()
    {
        future.cancel(true);
        cancelled = true;
    }

    public void actionPerformed(ActionEvent e)
    {
        if(cancelButtonVisible)
        {
            if(cancelAction != null)
            {
                cancelAction.actionPerformed(null);
            }
            else
            {
                stopCommand();
            }
        }
    }
    
    public void setBorder(Border border)
    {
        getRootPane().setBorder(border);
    }
    
    public Border getBorder()
    {
        return getRootPane().getBorder();
    }

    public JProgressBar getProgressBar()
    {
        return progressBar;
    }
    
    public JLabel getInfoLabel()
    {
        return lbInfo;
    }
    
    public JButton getCancelButton()
    {
        return btnCancel;
    }
    
    public Window getParent()
    {
        return parent;
    }

    public Runnable getCommand()
    {
        return command;
    }

    public String getStatusInfo()
    {
        return statusInfo;
    }

    public void setStatusInfo(String statusInfo)
    {
        this.statusInfo = statusInfo;
        lbInfo.setText("<html>" + statusInfo);
    }

    public ActionListener getCancelAction()
    {
        return cancelAction;
    }

    public void setCancelAction(Action cancelAction)
    {
        this.cancelAction = cancelAction;
    }
    
    public Action getFinishedAction()
    {
        return finishedAction;
    }

    public void setFinishedAction(Action finishedAction)
    {
        this.finishedAction = finishedAction;
    }

    public boolean isCancelButtonVisible()
    {
        return cancelButtonVisible;
    }

    public void setCancelButtonVisible(boolean cancelButtonVisible)
    {
        this.cancelButtonVisible = cancelButtonVisible;
        btnCancel.setVisible(cancelButtonVisible);
    }
    
    public boolean isMoveable()
    {
        return move.isMoveable();
    }

    public void setMoveable(boolean moveable)
    {
        move.setMoveable(moveable);
    }
    
    public boolean isCancelled()
    {
        return cancelled;
    }
    
    private class Task<V> extends FutureTask<V>
    {
        public Task(Runnable runnable, V result)
        {
            super(runnable, result);
        }
        
        protected void done()
        {
            executor.shutdown();
            executor = null;
            future = null;
            dispose();

            if(!cancelled && finishedAction != null)
            {
                finishedAction.actionPerformed(null);
            }
        }
    }
}